/*
 * Copyright 2010 Christian Matzat and others
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package net.matzat.android.hiberdroid.mapping;


import net.matzat.android.hiberdroid.annotation.Id;
import net.matzat.android.hiberdroid.exception.DuplicateMappingException;
import net.matzat.android.hiberdroid.exception.PropertyAccessException;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * This object holds the mapping of an entity (table).
 */
public class EntityMapping {

    /**
     * Name of the id property
     */
    public final static String ID_PROPERTY = "_id";

    private Class entityClass;
    private String table;
    private Map<String, ColumnMapping> columns;
    private List<String> columnNames;
    private String idProperty, idColumn;

    /**
     * create a new EntityMapping
     *
     * @param entityClass Class to create the mapping for.
     */
    public EntityMapping(Class entityClass) {
        this.entityClass = entityClass;
    }

    /**
     * Gets the database table of this entity
     *
     * @return the mapped database table
     */
    public String getTable() {
        return table;
    }

    /**
     * Sets the database table of this entity
     *
     * @param table database table to set
     */
    public void setTable(String table) {
        this.table = table;
    }

    /**
     * Gets the column mappings of this entity.
     *
     * @return column mapping
     */
    public Map<String, ColumnMapping> getColumns() {
        if (columns == null) {
            columns = new HashMap<String, ColumnMapping>();
        }
        return columns;
    }

    /**
     * Gets an array of all column names of this entity.
     *
     * @return array of column names
     */
    public String[] getColumnNames() {
        return columnNames != null ? columnNames.toArray(new String[columnNames.size()]) : new String[0];
    }

    /**
     * Gets the name of the id property of this entity.
     *
     * @return id property name
     */
    public String getIdProperty() {
        return idProperty;
    }

    /**
     * Gets the id column name of this entity.
     *
     * @return id column name
     */
    public String getIdColumn() {
        return idColumn;
    }

    /**
     * Add a column mapping to this entity.
     *
     * @param annotation Annotation on this mapping (if any)
     * @param type       type Class of this mapping
     * @param column     column name of this mapping
     * @param property   property name of this mapping
     */
    public void addColumn(Annotation annotation, Class type, String column, String property) {
        boolean autoincrement = false;
        boolean isId = false;
        if (annotation instanceof Id) {
            autoincrement = ((Id) annotation).autoincrement();
            isId = true;
        }
        if (!isId && ID_PROPERTY.equals(property)) {
            isId = true;
            autoincrement = true; // set autoincrement as default
        }
        if (columns == null) {
            columns = new HashMap<String, ColumnMapping>();
        }
        if (columns.get(property) != null) {
            throw new DuplicateMappingException(String.format("mapping for property %s  already set.", property));
        }
        ColumnType columnType = ColumnType.getByType(type);
        if (columnType != null) {
            if (isId && columnType != ColumnType.INTEGER) {
                throw new PropertyAccessException("id property must have type \"int\" or \"Integer\".");
            }
            columns.put(property, new ColumnMapping(column, columnType, isId, autoincrement));
        }
        addColumnName(column);
        if (isId) {
            idProperty = property;
            idColumn = column;
        }
    }

    private void addColumnName(String column) {
        if (columnNames == null) {
            columnNames = new ArrayList<String>();
        }
        columnNames.add(column);
    }

    /**
     * Gets the EntityClass of this mapping.
     *
     * @return entity class
     */
    public Class getEntityClass() {
        return entityClass;
    }

}
